#include <log4cxx/logger.h>

#include <json/json.h>

#include <arpa/inet.h>

#include "ProtoHelper.h"
#include "SocketHelper.h"

using namespace log4cxx;

static LoggerPtr g_logger = Logger::getLogger("ProtoHelper");

int recv_cmd(int fd, const int recv_timeout, const int max_cmd_size, string & buf) {
	if (fd < 0) {
		LOG4CXX_ERROR(g_logger, "recv command error, invalid fd:" << fd);
		return -1;
	}
	uint32_t cmd_len;
	int ret = recv_all(fd, &cmd_len, sizeof(cmd_len), recv_timeout);
	if (ret < 1) {
		LOG4CXX_ERROR(g_logger, "recv command len error, fd:" << fd << ", recv all ret:" << ret << \
				", recv_timeout:" << recv_timeout);
		return -2;
	}
	cmd_len = ntohl(cmd_len);
	if (max_cmd_size > 0 && (cmd_len > max_cmd_size)) {
		LOG4CXX_ERROR(g_logger, "recv command error, the cmd body is too big, max_cmd_size:" << max_cmd_size << \
				", requested size:" << cmd_len << ", recv_timeout:" << recv_timeout);
		return -3;
	}
	char * t = new char[cmd_len];
	ret = recv_all(fd, t, cmd_len, recv_timeout);
	if (ret < 1) {
		delete[] t;
		LOG4CXX_ERROR(g_logger, "recv command body error, fd:" << fd << ", recv all ret:" << ret << ", recv_timeout:" << \
				recv_timeout);
		return -4;
	}
	buf.append(t, cmd_len);
	delete[] t;
	return 1;
}

int recv_cmd(SocketConnPtr & conn, const int recv_timeout, const int max_cmd_size, string & buf) {
	int fd = conn.get();
	if (fd < 0) return -100;
	int ret = recv_cmd(fd, recv_timeout, max_cmd_size, buf);
	if (ret < 0) conn.set_network_error(true);
	return ret;
}

int send_cmd(int fd, const int send_timeout, const void * buf, const int len) {
	if (fd < 0 || NULL == buf || len < 0) {
		LOG4CXX_ERROR(g_logger, "send common error, invaid args, fd:" << fd);
		return -1;
	}
	if (0 == len) return 1;
	uint32_t cmd_len = htonl(len);
	int ret = send_all(fd, &cmd_len, sizeof(cmd_len), send_timeout);
	if (ret < 1) {
		LOG4CXX_ERROR(g_logger, "send command len error, fd:" << fd << ", send all ret:" << ret);
		return -2;
	}
	ret = send_all(fd, buf, len, send_timeout);
	if (ret < 1) {
		LOG4CXX_ERROR(g_logger, "send command body error, fd:" << fd << ", send all ret:" << ret);
		return -3;
	}
	return 2;
}

int send_cmd(SocketConnPtr & conn, const int send_timeout, const void * buf, const int len) {
	int fd = conn.get();
	if (fd < 0) return -100;
	int ret = send_cmd(fd, send_timeout, buf, len);
	if (ret < 0) conn.set_network_error(true);
	return ret;
}

int send_and_recv_cmd(SocketConnPtr & conn, const int send_timeout, const int recv_timeout, const void * send_buf, \
			const int send_len, const int max_recv_packet_size, string & recv_buf) {
		int fd = conn.get();
		if (fd < 0 || NULL == send_buf || send_len < 1) {
			LOG4CXX_ERROR(g_logger, "send and recv cmd error, invalid args");
			return -1;
		}
		int ret = send_cmd(conn, send_timeout, send_buf, send_len);
		if (ret < 0) {
			LOG4CXX_ERROR(g_logger, "send and recv cmd error, send error, fd:" << fd << ", ret:" << ret);	
			return -1;
		}
		ret = recv_cmd(conn, recv_timeout, max_recv_packet_size, recv_buf);
		if (ret < 0) {
			LOG4CXX_ERROR(g_logger, "send and recv cmd error, recv error, fd:" << fd << ", ret:" << ret);
			return -3;
		}
		return 1;
}

int parse_request(const char * request, const int len, json_object * & j, int & cmd_id) {
	if (NULL == request || len < 1) {
		LOG4CXX_ERROR(g_logger, "in parse request, cound not parse request, invalid args");
		return -1;
	}
	j = json_tokener_parse(request);
	if (is_error(j)) {
                LOG4CXX_ERROR(g_logger, "in parse request, parse json string error:" << request);
                return -2;
        }
        json_object * t = json_object_object_get(j, "CMD_ID");
        if (NULL == t) {
                LOG4CXX_ERROR(g_logger, "in parse request, get cmd id json object error:" << request);
                json_object_put(j);
                return -3;
        }
        cmd_id = json_object_get_int(t);
        if (cmd_id < 1) {
                LOG4CXX_ERROR(g_logger, "in parse request, cmd id is not valid, cmd id:" << cmd_id << ", request:" << request);
                json_object_put(j);
                return -4;
        }
        return 1;
}

int recv_and_parse_request(int fd, int socket_recv_timeout, int max_request_size, json_object * & j, int & cmd_id, \
			string & request) {
        int ret = recv_cmd(fd, socket_recv_timeout, max_request_size, request);
        if (ret < 0) {
                LOG4CXX_ERROR(g_logger, "in recv_and_parse_request, recv cmd error, ret:" << ret << ", fd:" << fd);
                return -1;
        }
	ret = parse_request(request.c_str(), request.size(), j, cmd_id);
	if (ret < 0) {
		LOG4CXX_ERROR(g_logger, "in recv_and_parse_request, parse json request error, ret:" << ret << ", fd:" << fd << \
				", request:" << request);
		return -2;
	}
	return 1;
}

string build_error_response(int result, const char * err_msg) {
	json_object * j = json_object_new_object();
        json_object * j_result = json_object_new_int(result);
        json_object_object_add(j, "RESULT", j_result);
  	json_object * j_err_msg = json_object_new_string(err_msg);
        json_object_object_add(j, "ERR_MSG", j_err_msg);
	string response = json_object_to_json_string(j);
	json_object_put(j);
	return response;
}

string build_ok_response(const char * tips) {
	json_object * j = json_object_new_object();
        json_object * j_result = json_object_new_int(OK_RESULT);
        json_object_object_add(j, "RESULT", j_result);
  	json_object * j_tips = json_object_new_string(tips);
        json_object_object_add(j, "TIPS", j_tips);
	string response = json_object_to_json_string(j);
	json_object_put(j);
	return response;
}
